﻿#include "as_poller.h"
#ifdef _WIN32
#include "wepoll.h"
#else
#include <sys/epoll.h>
#endif
#include "epoll_watch.h"
#include "as_channel.h"
#include <stdexcept>

enum ECHANNEL_TYPE
{
	ENEW = -1,
	EADD,
	EDEL
};

as::net::Poller::Poller(as::net::EpollWatch *ep_watch)
	:ep_watch_(ep_watch)
{

}

void as::net::Poller::Add(socket_fd fd, uint32_t events)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = events;
	if (epoll_ctl(ep_watch_->GetEpFd(), EPOLL_CTL_ADD, fd, &event) == -1) {
		throw std::runtime_error("Failed to add file descriptor to epoll");
	}
}

void as::net::Poller::Modify(socket_fd fd, uint32_t events)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = events;
	if (epoll_ctl(ep_watch_->GetEpFd(), EPOLL_CTL_MOD, fd, &event) == -1) {
		throw std::runtime_error("Failed to modify file descriptor in epoll");
	}
}

void as::net::Poller::Remove(socket_fd fd)
{
	if (epoll_ctl(ep_watch_->GetEpFd(), EPOLL_CTL_DEL, fd, nullptr) == -1) {
		throw std::runtime_error("Failed to remove file descriptor from epoll");
	}
}

void as::net::Poller::AddChannel(Channel *channel)
{
	epoll_event event;
	memset(&event, 0, sizeof(event));
	event.data.fd = channel->GetFd();
	event.events = channel->GetEvents();
	event.data.ptr = channel;
	if (epoll_ctl(ep_watch_->GetEpFd(), EPOLL_CTL_ADD, channel->GetFd(), &event) == -1) {
		throw std::runtime_error("Failed to add file descriptor to epoll");
	}
}

void as::net::Poller::ModifyChannel(Channel *channel)
{
	epoll_event event;
	event.data.fd = channel->GetFd();
	event.events = channel->GetEvents();
	event.data.ptr = channel;
	if (epoll_ctl(ep_watch_->GetEpFd(), EPOLL_CTL_MOD, channel->GetFd(), &event) == -1) {
		throw std::runtime_error("Failed to add file descriptor to epoll");
	}
}

void as::net::Poller::RemoveChannel(Channel *channel)
{
	auto fd = channel->GetFd();
	auto it = channel_map_.find(fd);
	if (it != channel_map_.end())
	{
		channel_map_.erase(it);
	}

	if (channel->GetType() == EADD)
	{
		epoll_event event;
		event.data.fd = channel->GetFd();
		event.events = channel->GetEvents();
		event.data.ptr = channel;
		if (epoll_ctl(ep_watch_->GetEpFd(), EPOLL_CTL_DEL, channel->GetFd(), &event) == -1) {
			throw std::runtime_error("Failed to add file descriptor to epoll");
		}
	}
	channel->SetType(ENEW);
}

void as::net::Poller::UpdateChannel(Channel *channel)
{
	int type = channel->GetType();
	socket_fd fd = channel->GetFd();
	auto it = channel_map_.find(fd);
	if (type == ENEW || type == EDEL)
	{
		if (type == ENEW)
		{
			if (it != channel_map_.end())
				return;
			channel_map_[fd] = channel;
		}
		else if (type == EDEL)
		{
			if (it == channel_map_.end())
				return;
			if (channel != channel_map_[fd])
				return;
		}
		channel->SetType(EADD);
		AddChannel(channel);
	}
	else if(type == EADD)
	{
		if (it == channel_map_.end())
			return;
		if (channel != channel_map_[fd])
			return;
		if (channel->HasFlag())
		{
			RemoveChannel(channel);
			channel->SetType(EDEL);
		}
		else
		{
			ModifyChannel(channel);
		}
	}
}

int as::net::Poller::Wait(epoll_event* events, int max_events, int timeout)
{
	int num_events = epoll_wait(ep_watch_->GetEpFd(), events, max_events, timeout);
	if (num_events == -1) 
	{
		LOG_WARNING << "epoll_wait num event:-1";	
	}
	return num_events;
}

void as::net::Poller::pool(std::vector<Channel*> &channelVec)
{
	//事件数不够大,有可能会错失fd触发
	static const int MAX_EVENT_NUM = 8192;
	static epoll_event evs[MAX_EVENT_NUM];
	int evNum = Wait(evs, MAX_EVENT_NUM, 30);
	for (int i = 0; i < evNum; ++i)
	{
		epoll_event &ev = evs[i];
		Channel *channel = (Channel*)ev.data.ptr;
		channel->SetEvents(ev.events);
		channelVec.push_back(channel);
	}
}

